/* eslint-disable @typescript-eslint/no-this-alias */
export type Callback = (e: Event) => void
export type MessageCallback<RT> = (e: RT) => void

interface Ioptions<RT> {
  url: string | null // 链接的通道的地址
  heartTime?: number // 心跳时间间隔
  heartMsg?: string // 心跳信息,默认为'ping'
  isReconnect?: boolean // 是否自动重连
  isRestory?: boolean // 是否销毁
  reconnectTime?: number // 重连时间间隔
  reconnectCount?: number // 重连次数 -1 则不限制
  openCb?: Callback // 连接成功的回调
  closeCb?: Callback // 关闭的回调
  messageCb?: MessageCallback<RT> // 消息的回调
  errorCb?: Callback // 错误的回调
}

/**
 * 心跳基类
 */

export class Heart {
  heartTimeOut!: number // 心跳计时器
  ServerHeartTimeOut!: number // 心跳计时器
  timeout = 5000
  // 重置
  reset(): void {
    clearTimeout(this.heartTimeOut)
    clearTimeout(this.ServerHeartTimeOut)
  }

  /**
   * 启动心跳
   * @param {Function} cb 回调函数
   */
  start(cb: Callback): void {
    // console.log('开启心跳')
    this.heartTimeOut = setTimeout((e: Event) => {
      cb(e)
      this.ServerHeartTimeOut = setTimeout((args: Event) => {
        cb(args)
        // 重新开始检测
        this.reset()
        this.start(cb)
      }, this.timeout)
    }, this.timeout)
  }
}

export default class Socket<T, RT> extends Heart {
  ws!: WebSocket

  reconnectTimer: any // 重连计时器
  reconnectCount = 10 // 变量保存，防止丢失

  options: Ioptions<RT> = {
    url: null, // 链接的通道的地址
    heartTime: 5000, // 心跳时间间隔
    heartMsg: 'ping', // 心跳信息,默认为'ping'
    isReconnect: true, // 是否自动重连
    isRestory: false, // 是否销毁
    reconnectTime: 5000, // 重连时间间隔
    reconnectCount: 5, // 重连次数 -1 则不限制
    openCb: (e: Event) => {
      console.log('连接成功的默认回调::::', e)
    }, // 连接成功的回调
    closeCb: (e: Event) => {
      console.log('关闭的默认回调::::', e)
    }, // 关闭的回调
    messageCb: (e: RT) => {
      console.log('连接成功的默认回调::::', e)
    }, // 消息的回调
    errorCb: (e: Event) => {
      console.log('错误的默认回调::::', e)
    } // 错误的回调
  }

  constructor(ops: Ioptions<RT>) {
    super()
    Object.assign(this.options, ops)
    this.create()
  }

  /**
   * 建立连接
   */
  create(): void {
    if (!('WebSocket' in window)) {
      throw new Error('当前浏览器不支持，无法使用')
    }
    if (!this.options.url) {
      throw new Error('地址不存在，无法建立通道')
    }
    this.ws = new WebSocket(this.options.url)
    this.onopen(this.options.openCb as Callback)
    this.onclose(this.options.closeCb as Callback)
    this.onmessage(this.options.messageCb as MessageCallback<RT>)
  }

  /**
   * 自定义连接成功事件
   * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
   * @param {Function} callback 回调函数
   */
  onopen(callback: Callback): void {
    this.ws.onopen = event => {
      // 连接成功后
      clearTimeout(this.reconnectTimer) // 清除重连定时器
      this.options.reconnectCount = this.reconnectCount // 计数器重置
      // 建立心跳机制，检测连接是否中断
      super.reset()
      super.start(() => {
        // 发送ping，进行检测
        this.send(this.options.heartMsg as string)
      })
      if (typeof callback === 'function') {
        callback(event)
      } else {
        typeof this.options.openCb === 'function' && this.options.openCb(event)
      }
    }
  }

  /**
   * 自定义关闭事件
   * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
   * @param {Function} callback 回调函数
   */
  onclose(callback: Callback): void {
    this.ws.onclose = event => {
      super.reset()
      // 当非正常销毁时，进行重连
      !this.options.isRestory && this.onReconnect()
      if (typeof callback === 'function') {
        callback(event)
      } else {
        typeof this.options.closeCb === 'function' && this.options.closeCb(event)
      }
    }
  }

  /**
   * 自定义错误事件
   * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
   * @param {Function} callback 回调函数
   */
  onerror(callback: Callback): void {
    this.ws.onerror = event => {
      if (typeof callback === 'function') {
        callback(event)
      } else {
        typeof this.options.errorCb === 'function' && this.options.errorCb(event)
      }
    }
  }

  /**
   * 自定义消息监听事件
   * 如果callback存在，调用callback，不存在调用OPTIONS中的回调
   * @param {Function} callback 回调函数
   */
  onmessage(callback: MessageCallback<RT>): void {
    this.ws.onmessage = (event: any) => {
      // 收到任何消息，重新开始倒计时心跳检测
      super.reset()
      super.start(() => {
        this.send(this.options.heartMsg as string)
      })
      if (typeof callback === 'function') {
        callback(event)
      } else {
        typeof this.options.messageCb === 'function' && this.options.messageCb(event)
      }
    }
  }

  /**
   * 自定义发送消息事件
   * @param {String} data 发送的文本
   */
  send(data: T | string): void {
    if (this.ws.readyState === this.ws.OPEN) {
      data = JSON.stringify(data)
      this.ws.send(data)
    } else if (this.ws.readyState === this.ws.CONNECTING) {
      const _self = this
      setTimeout(function () {
        _self.send(data)
      }, 1000)
    } else {
      this.ws.close()
      this.create()
    }
  }

  /**
   * 连接事件
   */
onReconnect(): void {
  const reconnectCount = this.options.reconnectCount ?? 0; // 使用默认值
  const reconnectTime = this.options.reconnectTime || 1000; // 设置默认重连时间

  if (reconnectCount > 0 || reconnectCount === -1) {
    this.reconnectTimer = setTimeout(() => {
      this.create();
      if (reconnectCount !== -1) {
        this.options.reconnectCount = reconnectCount - 1; // 递减重连次数
      }
    }, reconnectTime);
  } else {
    // 当 reconnectCount 等于 0 时，清除重连定时器并重置次数
    clearTimeout(this.reconnectTimer);
    this.options.reconnectCount = this.reconnectCount; // 确保 this.reconnectCount 已定义
  }
}


  /**
   * 销毁
   */
  destroy(): void {
    super.reset()
    clearTimeout(this.reconnectTimer) // 清除重连定时器
    this.options.isRestory = true
    this.ws.close()
  }
}

